-
Notifications
You must be signed in to change notification settings - Fork 32
/
基本类型与包装类梳理及源码.md
444 lines (324 loc) · 15 KB
/
基本类型与包装类梳理及源码.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
### 基本与包装类
- 基本类型有默认值,而包装类型初始为null
- 所有的POJO类必须使用包装类型,在rpc方法中定义参数和返回值的类型用包装类,因为默认值不代表空值,比如0不代表该属性为空
- 泛型调用函数必须用包装类,因为其参数类型都是Object。但是反射调用方法,其方法参数可以为基本类型,在invoke的java doc中说明了原始类型会和引用类型自动转换。
- 在本地变量推荐使用基本类型。
- 什么时候该用包装类,什么时候用基本类型,看基本的业务来定:这个字段允不允许null值或者参数为Object,则必然要用封装类,否则值类型就可以了。
#### 数据类型
八种基本类型
- 整数:byte(8-bit)、short(16-bit)、int(32-bit)、long(64bit)初始值为0。
- 浮点型:float(32-bit)、double(64-bit)初始值为0.0
- 字符:char(16-bit)初始值为空格,即' ',如果输出,在Console上是看不到效果的。
- Java采用unicode,2个字节来表示一个字符,1个字节等于16位
- 布尔:boolean初始值为false
包装类
- Integer 、Long、Short、Byte、Character、Double、Float、Boolean
- BigInteger、BigDecmail
- 没有相对应的基本类型,主要应用于高精度的运算
- BigInteger 支持任意精度的整数, BigDecimal支持任意精度带小数点的运算。
- 原理
- 精度丢失
不是所有的十进制数都能转化为有限位二进制数的。
1、任意十进制整数可以转化为有限位数的二进制整数。
如123=64+32+16+8+2+1,转化为二进制整数是1111011。
2、能分解为以(1/2)^n为单位的十进制小数,可以转化为有限位数的二进制小数。
如十进制数:13/16=0.8125,它可以是拆成:13/16=1/2+1/4+1/16,或者直接可以看作是13个 1/16所组成。而1/2,1/4,1/16这些数都是符合(1/2)^n形式的数。所以13/16转化为4位二进制 小数:0.1101。
3、上述情形以外的十进制数都不能转化为有限位数的二进制数。
如十进制小数0.7,转化为二进制小数是:0.1011001100110......,循环节是0110。
当第三种情况的小数出现的时候,就会出现计算的精度误差,BigDecimal的原理很简单,就是将小数扩大N倍,转成整数后再进行计算,同时结合指数,得出没有精度损失的结果。
- 源码(BigDecimal)
```
public class BigDecimal {
//值的绝对long型表示(数值整体扩大(10^scale)倍到不含小数)
private final transient long intCompact;
//值的小数点后的位数
private final int scale;
//先double转成String,再String转成BigDecimal
private transient String stringCache;
private final BigInteger intVal;
//值的有效位数,不包含正负符号
private transient int precision;
//加、减、乘、除、绝对值
public BigDecimal add(BigDecimal augend) {}
public BigDecimal subtract(BigDecimal subtrahend) {}
public BigDecimal multiply(BigDecimal multiplicand) {}
public BigDecimal divide(BigDecimal divisor) {}
public BigDecimal abs() {}
}
```
在运算完后调用静态工厂方法:
```
// sum:小数扩大rscale倍long型数
// rsacle:小数点后的位数
BigDecimal.valueOf(sum,rscale);
```
- 源码(BigInteger)
```
/**
* BigInteger是按位存储的数据,一个10进制数占3.3bit,mag存储的数据全是正数,符号单独用signum保存,如果数据的总位数超过Integer.MAX_VAULE 则会溢出。
*/
public class BigInteger extends Number implements Comparable<BigInteger> {
// 标志为正负或0
final int signum;
// 存储数据每一位的二进制数,一个int占32bit,最大可以用Integer.MAX_VAULE个bit
final int[] mag;
....
}
```
```
/**
* add先比较符号是否同,不同的话先比较两个数mag的大小,然后相减取较大值的符号
*/
public BigInteger add(BigInteger val) {
if (val.signum == 0)
return this;
if (signum == 0)
return val;
if (val.signum == signum)
return new BigInteger(add(mag, val.mag), signum);
int cmp = compareMagnitude(val);
if (cmp == 0)
return ZERO;
int[] resultMag = (cmp > 0 ? subtract(mag, val.mag)
: subtract(val.mag, mag));
resultMag = trustedStripLeadingZeroInts(resultMag);
return new BigInteger(resultMag, cmp == signum ? 1 : -1);
}
```
异同
- 八大基本类型不是对象。
- 声明方式的不同,基本类型无需通过new关键字来创建,而封装类型需new关键字。
- 原生类型在作为参数传递到方法中时使用的是“值传递”的方式,而对象作为参数传递到方法中时使用的是“传引用”的方式。
- 存储方式及位置的不同,
- 基本类型是直接存储变量的值保存在堆栈中能高效的存取
- 在方法中声明的变量,即该变量是局部变量,存在虚拟机栈
- 在类中声明的变量是成员变量,也叫全局变量,放在堆中
- 封装类型需要通过引用指向实例,具体的实例保存在堆中
- 初始值的不同,封装类型的初始值为null,基本类型的的初始值视具体的类型而定,比如int类型的初始值为0,boolean类型为false;
- 使用方式的不同,比如与集合类合作使用时只能使用包装类型。
#### 长度
基本类型:Byte二进制位数:8
包装类:java.lang.Byte
最小值:Short.MIN=-128 (-2的7此方)
最大值:Short.MAX=127 (2的7次方-1)
基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768 (-2的15此方)
最大值:Short.MAX_VALUE=32767 (2的15次方-1)
基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE= -2147483648 (-2的31次方)
最大值:Integer.MAX_VALUE= 2147483647 (2的31次方-1)
基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808 (-2的63次方)
最大值:Long.MAX_VALUE=9223372036854775807 (2的63次方-1)
基本类型:float 二进制位数:32包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45 (2的-149次方)
最大值:Float.MAX_VALUE=3.4028235E38 (2的128次方-1)
基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324 (2的-1074次方)
最大值:Double.MAX_VALUE=1.7976931348623157E308 (2的1024次方-1)
**float和double说明(IEEE754)**
float:符号位(1bit),指数位(8bit),尾数位(23bit)。
- 取值范围:-2^[128] ~~ -2^[-126 - 23] , 0 , 2^[-126 - 23] ~~ 2^[128]
- 指数位:指数位范围为(255-127 , 0 -127)
- 精度范围(看小数(尾数)部分):2^(-23) = 1.1920929E-7,所以float的精度为6~7位,能保证6位为绝对精确,7位一般也是正确的,8位就不一定了(但不是说8位就绝对不对了)
double:符号位(1bit),指数位(11bit),尾数位(52bit)
- 取值范围:-2^[1024] ~~ -2^[-1022 - 52] , 0 , 2^[-1022 - 52] ~~ 2^[1024]
- 指数位:指数位范围为(2047-1023 , 0 -1023)
- 精度范围(看小数(尾数)部分):2^(-52) = 2.220446049250313E-16,所以double精度是15~16,能保证15,一般16位。
#### 缓存池
--------------------------------------缓存自身所有值--------------------------------------
**Boolean**
boolean 的包装类型,缓存最简单,直接定义为静态常量就可以
```
public final class Boolean implements java.io.Serializable,Comparable<Boolean>{
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}
```
**Character**
Character 静态内部类 CharacterCache(ASCII 码范围为 0-127, 这里只缓存ASCII 码范围的char)
```
public final class Character implements java.io.Serializable, Comparable<Character> {
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
private static class CharacterCache {
private CharacterCache(){}
static final Character cache[] = new Character[127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Character((char)i);
}
}
}
```
**Byte**
Byte静态内部类ByteCache(byte 的范围是: -128<=x<=127,所以byte 一定是从缓存里面获取)
```
public final class Byte extends Number implements Comparable<Byte> {
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
private static class ByteCache {
private ByteCache(){}
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
}
}
}
```
--------------------------------------缓存一定范围(256)--------------------------------------
**Short**
Short静态内部类ShortCache(缓存 -128<=x<=127 )
```
public final class Short extends Number implements Comparable<Short> {
public static Short valueOf(short s) {
final int offset = 128;
int sAsInt = s;
if (sAsInt >= -128 && sAsInt <= 127) { // must cache
return ShortCache.cache[sAsInt + offset];
}
return new Short(s);
}
private static class ShortCache {
private ShortCache(){}
static final Short cache[] = new Short[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Short((short)(i - 128));
}
}
}
```
**Integer**
IntegerCache 的范围是可以配置的,从源码中可以看出来 最低值一定是-128 是不可以修改的,但是上限值high 是可以修改的(通过jvm参数: -Djava.lang.Integer.IntegerCache.high=1024 修改为1024)。
```
public final class Integer extends Number implements Comparable<Integer> {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
}
```
**Long**
Long静态内部类LongCache(缓存 -128<=x<=127 )
```
public final class Long extends Number implements Comparable<Long> {
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
private static class LongCache {
private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
}
}
}
```
--------------------------------------不被缓存--------------------------------------
**Float**
```
public static Float valueOf(float f) {
return new Float(f);
}
}
```
**Double**
```
public final class Double extends Number implements Comparable<Double> {
public static Double valueOf(double d) {
return new Double(d);
}
}
```
#### 自动拆箱装箱
为啥有包包装类
- 第一:如果你想在方法体内更新primitive类型的值,必须要使用primitive对应的object,因为前者使用的值传递,后者使用的是引用传递。
- 第二:java.util内操作的都是对象,如果没有PWC,会让程序员在使用这些工具类操作primitive类型时编写额外的代码。集合框架中的数据结构,比如ArrayList和Vector,也是只能操作对象。
- 第三:如果对象的属性有值类,在反序列化时无法把null值赋给基本类型
- 第四:多线程中也必须使用对象来完成各种同步操作。
- 第五:从设计理念上,在Java中,万物皆对象,为primitive类型设计出与之匹配的对象类型,更能让编程体验与设计理念融为一体!
装箱拆箱
- 装箱
```\
// 手动装箱
Integer i = Integer.valueOf(1);
```
```
// 自动装箱
Integer i = 1;
List<Integer> list = new ArrayList();
list.add(1);
```
- 拆箱
```
//手动拆箱
int i = integer.intValue()
```
```
int i = integer;
```
#### 补充
**位移符号**
- \>>:带符号右移。正数右移高位补0,负数右移高位补1。
比如:4 >> 1,结果是2;
-4 >> 1,结果是-2。
- \>>>:无符号右移。无论是正数还是负数,高位通通补0。
对于正数而言,>>和>>>没区别。
对于负数而言,-2 >>> 1,结果是2147483647(Integer.MAX_VALUE)
总结
- 对于位移前,全部都是转化为int进行的,所以正数高位补0(补够32位),负数补1(补够32位)
- 对于进行位移导致缺位
- 补高位:\>>正数高位补0,负数高位补1,符号位不移动;对于>>> 全都补0,符号位移动
- 补低位:<<低位全都补0,符号位不移动、<<<低位全部补0,符号位移动
- 对于计算机来说存储的都是补码,所以位移操作作用的都是补码
- 对于通过二进制数读取的数值是补码,要转为原码,比如底层二进制数为 1110 0000 ,读取的值为-32